<!DOCTYPE html>
<html lang="en-us">
<head><head>
    <meta name="referrer" content="no-referrer"/>
    <meta name="google-site-verification" content="9vIieCe-Qpd78QOmBl63rGtIVbhY6sYyuxX3j8XWBA4" />
    <meta name="baidu-site-verification" content="LRrmH41lz7" />
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <meta name="description" content="基于git的工作流方案实践">
    
    <meta name="keyword"  content="暴走的初号机, shinji3887, 暴走的初号机的网络日志, 暴走的初号机的博客, shinji3887 Blog, 博客, 个人网站, 互联网, Web, 云原生, PaaS, Istio, Kubernetes, 微服务, Microservice">
    <link rel="shortcut icon" href="/img/favicon.ico">

    <title>Git的代码分支策略实践-同步率400%</title>

    <link rel="canonical" href="/post/github-flow-practice/">

    <link rel="stylesheet" href="https://lupeier.cn-sh2.ufileos.com/iDisqus.min.css"/>
	
    
    <link rel="stylesheet" href="https://lupeier.cn-sh2.ufileos.com/bootstrap.min.css">

    
    <link rel="stylesheet" href="https://lupeier.cn-sh2.ufileos.com/hux-blog.min.css">

    
    <link rel="stylesheet" href="https://lupeier.cn-sh2.ufileos.com/syntax.css">

    
    <link rel="stylesheet" href="https://lupeier.cn-sh2.ufileos.com/zanshang.css">

    
    <link href="/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    
    
    <script src="https://lupeier.cn-sh2.ufileos.com/jquery.min.js"></script>
    
    
    <script src="https://lupeier.cn-sh2.ufileos.com/bootstrap.min.js"></script>
    
    
    <script src="https://lupeier.cn-sh2.ufileos.com/hux-blog.min.js"></script>
</head>
</head>

<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
    <div class="container-fluid">
        
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">L&#39;s Blog</a>
        </div>

        
        
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
                    
                    <li>
                        <a href="categories/tech">tech</a>
                    </li>
                    
                    <li>
                        <a href="categories/tips">tips</a>
                    </li>
                    
                    <li>
                        <a href="/about">About</a>
                    </li>
                    
                </ul>
            </div>
        </div>
        
    </div>
    
</nav>
<script>
    
    
    
    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        
            $navbar.className = " ";
            
            setTimeout(function(){
                
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>




<style type="text/css">
    header.intro-header{
        background-image: url('https://lupeier.cn-sh2.ufileos.com/blur-brainstorming-chatting-1881333.jpg')
    }
</style>
<header class="intro-header" >
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <div class="tags">
                       
                       <a class="tag" href="/tags/git" title="Git">
                           Git
                        </a>
                        
                       <a class="tag" href="/tags/devops" title="DevOps">
                           DevOps
                        </a>
                        
                    </div>
                    <h1>Git的代码分支策略实践</h1>
                    <h2 class="subheading"></h2>
                    <span  class="meta">Posted by L&#39; on Monday, November 26, 2018
                        
                    </span>
					<br>
                </div>
            </div>
        </div>
    </div>
</header>




<article>
    <div class="container">
        <div class="row">

            
            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                post-container">

        		
                        <header>
                        <h2>TOC</h2>
                        </header>
                        <nav id="TableOfContents"></nav>
        		
        		<blockquote>
<p>目前主流的git工作流模式有git flow、github flow、gitlab flow这几种，采用不同的代码分支策略，意味着实施不同的代码集成与上线流程，这会影响整个研发团队每日的协作方式，因此研发团队通常需要认真的选择适合自己的分支策略。</p>
</blockquote>
<p>对于Git flow的工作流程，可以参考下面这篇（我个人比较推崇阮老师的博客，里面干货很多）
<a href="http://www.ruanyifeng.com/blog/2015/12/git-workflow.html">Git工作流程-阮一峰的博客</a></p>
<p>这篇是gitlab的CEO Sytse Sijbrandij写的一篇关于gitlab flow的blog，里面其实三种策略都有写到，并总结了优缺点，官方推荐
<a href="https://about.gitlab.com/2014/09/29/gitlab-flow/">GitLab Flow</a></p>
<p>这篇是上面那篇gitlab flow博客的中文翻译，翻的很不错，英文不好的可以直接看这篇
<a href="http://www.15yan.com/topic/yi-dong-kai-fa-na-dian-shi/6yueHxcgD9Z/">GitLab Flow的使用</a></p>
<p>代码管理策略主要有主干开发和功能分支开发这两种，功能分支开发又分为以下几种主流模型，几种模型各有所长，下面简述一下</p>
<ul>
<li>git flow：出现时间最早，基于git的workflow的开山鼻祖，可以说给出了一个git flow的最佳实践，缺点是流程比较复杂，release branch和hotfix branch几乎没人使用，另外需要长期维护master和dev两个分支，在规模不大的场景下维护成本比较高</li>
<li>github flow：相当精简，只有master主干和feature branch这两种，结构相当清晰，缺点是master默认为当前上线的最新版本，在对于版本管理要求比较复杂的场景下灵活性不足</li>
<li>gitlab flow：出现的最晚，可以说集合了前两家的长处，既保证只有一个长期主干，结构清晰，同时也定义了不同场景下的branch，增强了灵活性。</li>
</ul>
<p>像谷歌、脸书这样的互联网大咖，现在采用的都是主干开发模式，即只有一个master主干，所有的代码合并都在主干上完成，优点是结构清晰，特别适合快速的CI/CD流程，但是对于团队的技术能力要求非常高。目前国内的互联网公司，一般都采用功能分支（feature branch）的开发模式，在开发人员能力良莠不齐的情况下，相对来说对于代码的掌控能力比较好。
对于几种模式的使用场景，如下表所列：（列表来源：极客时间王潇俊的持续交付专题）</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>情况</th>
<th>适合的分支策略</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>开发团队能力很强，需要快速的CI/CD能力</td>
<td>主干开发</td>
</tr>
<tr>
<td>2</td>
<td>有预定的发布周期，需要执行严格的发布流程</td>
<td>Git Flow</td>
</tr>
<tr>
<td>3</td>
<td>随时集成随时发布，分支merge后经过评审就可以自动发布</td>
<td>Github Flow</td>
</tr>
<tr>
<td>4</td>
<td>无法控制准确的发布时间，但又要求不停集成</td>
<td>Gitlab Flow（带生产分支）</td>
</tr>
<tr>
<td>5</td>
<td>需要逐个通过各个测试环境验证</td>
<td>Gitlab Flow（带环境分支）</td>
</tr>
<tr>
<td>6</td>
<td>需要对外发布和维护不同版本</td>
<td>Git Flow（带版本分支）</td>
</tr>
</tbody>
</table>
<p>其实不管是选择哪种分支策略，都是基于Feature Driven Development（FDD）原则进行项目管理，既先要有issue（需求）输入，建立对应的功能分支（feature branch）再进行代码开发，完成之后合入主干，同时删除该功能分支。
个人认为对于中小型的团队来说，github flow已经足够完成需求，并且由于微服务架构的流行，一般工程都已经按照服务进行拆分，每个服务一个repo，需要同时进行复杂版本管理的场景不是很多。而现在一般在内网部署都是采取gitlab，所以下面就来说说怎么在gitlab里实施github flow（听起来有点绕口-__-）</p>
<p>1.repo的maintainer在issue上创建问题（新功能或者bug fix），指定issue的接收人或者由团队成员自己认领，并创建对应的feature branch，这点gitlab比较强大，可以自动创建issue对应的功能分支，github好像没有这个功能，需要开发人员手工创建并关联到issue
<img src="https://upload-images.jianshu.io/upload_images/14871146-5b602ddf4e2af565.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git.png"></p>
<p>2.负责处理的issue的同事Bob checkout这个特性分支（首次开发的话也可以clone这个仓库并切换到该功能分支）,注意本地的分支名字需要和远程保持一致，否则push的时候会有问题</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">I:<span style="color:#ae81ff">\m</span>saworkspace<span style="color:#ae81ff">\b</span>ocsh-service-base&gt;git checkout -b 1-test-issue origin/1-test-issue
Switched to a new branch <span style="color:#e6db74">&#39;1-test-issue&#39;</span>
Branch <span style="color:#e6db74">&#39;1-test-issue&#39;</span> set up to track remote branch <span style="color:#e6db74">&#39;1-test-issue&#39;</span> from <span style="color:#e6db74">&#39;origin&#39;</span>.
</code></pre></div><p>使用<code>git branch</code>查看，发现已经checkout成功并切换到<code>1-test-issue</code>这个分支上了,并且本地的<code>1-test-issue</code>和远程仓库的<code>1-test-issue</code>分支已经建立了追踪关系</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">I:<span style="color:#ae81ff">\m</span>saworkspace<span style="color:#ae81ff">\b</span>ocsh-service-base&gt;git branch
* 1/test/issue
  master
</code></pre></div><p>3.Bob在特性分支上进行工作，并每日push代码</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">I:<span style="color:#ae81ff">\m</span>saworkspace<span style="color:#ae81ff">\b</span>ocsh-service-base&gt;git commit -am <span style="color:#e6db74">&#34;test issue track&#34;</span>
<span style="color:#f92672">[</span>1-test-issue 08972ca<span style="color:#f92672">]</span> test issue track
 <span style="color:#ae81ff">1</span> file changed, <span style="color:#ae81ff">1</span> insertion<span style="color:#f92672">(</span>+<span style="color:#f92672">)</span>

I:<span style="color:#ae81ff">\m</span>saworkspace<span style="color:#ae81ff">\b</span>ocsh-service-base&gt;git push
Enumerating objects: 17, <span style="color:#66d9ef">done</span>.
Counting objects: 100% <span style="color:#f92672">(</span>17/17<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Delta compression using up to <span style="color:#ae81ff">4</span> threads
Compressing objects: 100% <span style="color:#f92672">(</span>6/6<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Writing objects: 100% <span style="color:#f92672">(</span>9/9<span style="color:#f92672">)</span>, <span style="color:#ae81ff">632</span> bytes | 316.00 KiB/s, <span style="color:#66d9ef">done</span>.
Total <span style="color:#ae81ff">9</span> <span style="color:#f92672">(</span>delta 3<span style="color:#f92672">)</span>, reused <span style="color:#ae81ff">0</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>
remote:
remote: To create a merge request <span style="color:#66d9ef">for</span> 1-test-issue, visit:
remote:   http://22.196.66.28/7310754/bocsh-service-base/merge_requests/new?merge_request%5Bsource_branch%5D<span style="color:#f92672">=</span>1-test-issue
remote:
To http://22.196.66.28/7310754/bocsh-service-base.git
   498d77f..08972ca  1-test-issue -&gt; 1-test-issue
</code></pre></div><p>这边注意Bob直接使用了git commit -am参数
这个相当于</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">git add .
git commit -m
</code></pre></div><p>同时因为指定了跟踪关系，所以可以直接用<code>git push</code>命令进行推送，git会自动把当前的活动分支的代码push到远程的对应分支上去（还记得前面说的建立对应跟踪关系吗），同时git提示我们可以create a merge request去申请把1-test-issue分支合并入master主干
同时项目管理人员（一般是这个repo的owner或者maintainer）还可以通过点击issue页面对应的分支，查看该分支是否被认领，以及该分支的工作进度（Bob，你有木有每天认真干活啊）
<img src="https://upload-images.jianshu.io/upload_images/14871146-6b35c7f6fe3ee892.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git2.png"></p>
<p><img src="https://upload-images.jianshu.io/upload_images/14871146-eab127d1b97e7421.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git3.png"></p>
<p>4.Bob认为功能完成并本地测试通过，在gitlab页面上提交一个merge request
<img src="https://upload-images.jianshu.io/upload_images/14871146-47dc489b9013fea6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git4.png">
title这边填写本次MR的标题，description这边填写主要提交的内容，注意必须要包含Closes #1关键字，这样在merge成功后会自动关系关联的issue（gitlab真的很方便，github里面这些都是要自己手写的）</p>
<p><img src="https://upload-images.jianshu.io/upload_images/14871146-b45ae2f86db166fb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git5.png">
这里的选项可以指定审核人，给MR打标签等等操作，注意有两个选项</p>
<ul>
<li>Remove source branch when merge request is accepted.
这个会在合并成功后自动删除对应的功能分支</li>
<li>Squash commits when merge request is accepted.</li>
</ul>
<p>在合并后自动创建一个commit节点，因为git的合并有两种模式，快进模式只会直接改变HEAD指针的位置，不会创建commit id，这边为了流程清晰还是建议创建一个commit id
4.maintainer审查代码，确认ok后将合并入master，同时删除该特性分支，并根据#close这样的关键字自动关闭issue
<img src="https://upload-images.jianshu.io/upload_images/14871146-836f7827a5da2da4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git6.png">
如果项目集成了gitlab ci的话，这边还能看到持续集成的结果，从上面的页面看到持续集成pipeline的测试和构建也都通过了，点击change选项卡可以查看改动的地方，审核无误后点击merge按钮就可以合并入master了
<img src="https://upload-images.jianshu.io/upload_images/14871146-3a8df01e059ac82d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git7.png">
这边看到已经成功合并入主干，同时这个特性分支也自动删除，并且自动关闭了关联的issue</p>
<p>5.如果是准备上线的版本，那在合并成功后还需要打tag，以便于版本的追踪，我们这边设置为1.0版本
<img src="https://upload-images.jianshu.io/upload_images/14871146-d73935f9933b7dc0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git8.png">
版本打成功后，在tag页面可以看到历史版本记录以及对应的commit id
<img src="https://upload-images.jianshu.io/upload_images/14871146-a4ebf3e430acc942.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="git9.png"></p>
<p>总结：基于issue的项目管理（FDD），非常便于项目的跟踪和代码审核、版本历史检索等，中小型团队建议实施github flow工作流模型。</p>

        
                
        
              <hr>
              <ul class="pager">
                  
                  <li class="previous">
                      <a href="/post/linux-npm-install-eacces/" data-toggle="tooltip" data-placement="top" title="linux下使用npm install报EACCES的解决方法">&larr; Previous Post</a>
                  </li>
                  
                  
                  <li class="next">
                      <a href="/post/restful-api-design/" data-toggle="tooltip" data-placement="top" title="浅淡RESTful api设计规范">Next Post &rarr;</a>
                  </li>
                  
              </ul>
  
              

<div id="gitalk-container"></div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script src="/js/md5.min.js"></script>
<script>
	const gitalk = new Gitalk({
	  clientID: 'aff2580d8cc58af83367',
	  clientSecret: '747547f5f87fcc5145b847ab76a498d7e501319f',
	  repo: 'comment',
	  owner: 'shinji3887',
	  admin: ['shinji3887'],
	  id: md5(location.pathname),      
	  distractionFreeMode: false  
	})

	gitalk.render('gitalk-container')
</script>


            </div>
            
            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                
                <section>
                    <hr class="hidden-sm hidden-xs">
                    <h5><a href="/tags/">FEATURED TAGS</a></h5>
                    <div class="tags">
                     
                    
                        
                            <a href="/tags/api-gateway" title="api-gateway">
                                api-gateway
                            </a>
                        
                    
                        
                    
                        
                            <a href="/tags/cloud-native" title="cloud-native">
                                cloud-native
                            </a>
                        
                    
                        
                            <a href="/tags/devops" title="devops">
                                devops
                            </a>
                        
                    
                        
                            <a href="/tags/docker" title="docker">
                                docker
                            </a>
                        
                    
                        
                    
                        
                    
                        
                    
                        
                    
                        
                            <a href="/tags/istio" title="istio">
                                istio
                            </a>
                        
                    
                        
                    
                        
                            <a href="/tags/kubernetes" title="kubernetes">
                                kubernetes
                            </a>
                        
                    
                        
                            <a href="/tags/microservice" title="microservice">
                                microservice
                            </a>
                        
                    
                        
                    
                        
                    
                        
                    
                        
                    
                        
                            <a href="/tags/restful" title="restful">
                                restful
                            </a>
                        
                    
                        
                    
                        
                            <a href="/tags/servicemesh" title="servicemesh">
                                servicemesh
                            </a>
                        
                    
                        
                            <a href="/tags/spring-cloud" title="spring-cloud">
                                spring-cloud
                            </a>
                        
                    
                        
                            <a href="/tags/vue" title="vue">
                                vue
                            </a>
                        
                    
                        
                    
                        
                    
                    </div>
                </section>

                
                <hr>
                <h5>FRIENDS</h5>
                <ul class="list-inline">
                    
                        <li><a target="_blank" href="https://skyao.io/">小剑的博客</a></li>
                    
                        <li><a target="_blank" href="https://zhaohuabing.com/">huabing的博客</a></li>
                    
                        <li><a target="_blank" href="http://blog.didispace.com/">程序猿DD的博客</a></li>
                    
                </ul>
            </div>
        </div>
    </div>
</article>




<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <ul class="list-inline text-center">
                   
                   <li>
                       <a href="" rel="alternate" type="application/rss+xml" title="L&#39;s Blog" >
                           <span class="fa-stack fa-lg">
                               <i class="fa fa-circle fa-stack-2x"></i>
                               <i class="fa fa-rss fa-stack-1x fa-inverse"></i>
                           </span>
                       </a>
                   </li>
                   
                    
                    <li>
                        <a href="mailto:18016380795@163.com">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-envelope fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
		    
                    
                    
                    
                    

                    

		    
                    
                    <li>
                        <a target="_blank" href="/link%20of%20wechat%20QR%20code%20image">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-wechat fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
		    
                    
                    <li>
                        <a target="_blank" href="https://github.com/shinji3887">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
		    
                    
                    <li>
                        <a target="_blank" href="https://www.linkedin.com/in/lupeier">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-linkedin fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
		    
                </ul>
		<p class="copyright text-muted">
                    Copyright &copy; L&#39;s Blog , 2018
                    <br>
                    <br>
                    <a href="http://icp.chinaz.com/info?q=lupeier.com" target="_blank">备案号：沪ICP备19022667号-1</a>                    
                </p>
            </div>
        </div>
    </div>
</footer>



<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>






<script>
    
    if($('#tag_cloud').length !== 0){
        async("/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>


<script>
    async("/js/fastclick.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>


<script>
    (function(){
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https'){
       bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
      }
      else{
      bp.src = 'http://push.zhanzhang.baidu.com/push.js';
      }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>







</body>
</html>
